{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Generalized Linear Models Comparison\n",
    "\n",
    "All three **Generalized Linear Models (GLM)** currently available in scikit-learn have been integrated for FHE in Concrete ML: PoissonRegressor, GammaRegressor and TweedieRegressor. Choosing the best GLM directly depends on the initial problem one has to solve, and more specifically, how the target values are distributed.  Detailed information about those models can be found in their associated [documentation](https://scikit-learn.org/stable/modules/classes.html#:~:text=Generalized%20linear%20models%20(GLM)) as well as their [user guide](https://scikit-learn.org/stable/modules/linear_model.html#:~:text=vs%20other%20solvers%E2%80%9D-,1.1.12.%20Generalized%20Linear%20Regression).\n",
    "\n",
    "This notebook shows how to train these models and run them in FHE using Concrete ML. It is highly inspired by this [notebook](https://scikit-learn.org/stable/auto_examples/linear_model/plot_tweedie_regression_insurance_claims.html) from scikit-learn so that users can appreciate how similar Concrete ML is to the library.\n",
    "\n",
    "A deeper analysis on the impact of quantization and FHE computations over a model's performance in comparison to its scikit-learn equivalent one is available in the [PoissonRegressor notebook](PoissonRegression.ipynb)."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Import libraries\n",
    "\n",
    "We import scikit-learn libraries, Concrete quantization tools as well as the Poisson, Gamma and Tweedie regression model from both scikit-learn and Concrete ML:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Source : https://scikit-learn.org/stable/auto_examples/linear_model/plot_tweedie_regression_insurance_claims.html # noqa # pylint: disable=line-too-long\n",
    "\n",
    "# Authors: Christian Lorentzen <lorentzen.ch@gmail.com>\n",
    "#          Roman Yurchak <rth.yurchak@gmail.com>\n",
    "#          Olivier Grisel <olivier.grisel@ensta.org>\n",
    "# Modified to integrate Concrete ML functions by Zama\n",
    "# License: BSD 3 clause\n",
    "\n",
    "import sys\n",
    "import time\n",
    "from collections import defaultdict\n",
    "from timeit import default_timer as timer\n",
    "\n",
    "import numpy as np\n",
    "import sklearn\n",
    "from sklearn.compose import ColumnTransformer\n",
    "from sklearn.datasets import fetch_openml\n",
    "from sklearn.linear_model import GammaRegressor as SklearnGammaRegressor\n",
    "from sklearn.linear_model import PoissonRegressor as SklearnPoissonRegressor\n",
    "from sklearn.linear_model import TweedieRegressor as SklearnTweedieRegressor\n",
    "from sklearn.metrics import mean_gamma_deviance, mean_poisson_deviance, mean_tweedie_deviance\n",
    "from sklearn.model_selection import train_test_split\n",
    "from sklearn.pipeline import make_pipeline\n",
    "from sklearn.preprocessing import (\n",
    "    FunctionTransformer,\n",
    "    KBinsDiscretizer,\n",
    "    OneHotEncoder,\n",
    "    StandardScaler,\n",
    ")\n",
    "\n",
    "from concrete.ml.sklearn import GammaRegressor as ConcreteGammaRegressor\n",
    "from concrete.ml.sklearn import PoissonRegressor as ConcretePoissonRegressor\n",
    "from concrete.ml.sklearn import TweedieRegressor as ConcreteTweedieRegressor"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "%matplotlib inline\n",
    "\n",
    "import matplotlib.pyplot as plt\n",
    "from IPython.display import display"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Fetching and cleaning the data\n",
    "\n",
    "First, we download the data. It is composed of two data-sets that will be joined together:\n",
    "- the original one containing most risks features for 677,991 policies, each identified by an unique ID\n",
    "- an additional one only containing amounts claimed for some of those policies, matching the same IDs\n",
    "\n",
    "Since the regressors are not meant to be used for similar target values distributions, the second data-set will mainly be used for the Gamma regression model."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Getting the original data-set containing the risk features\n",
    "# Link: https://www.openml.org/d/41214\n",
    "risks_data, _ = fetch_openml(\n",
    "    data_id=41214, as_frame=True, cache=True, data_home=\"~/.cache/sklearn\", return_X_y=True\n",
    ")\n",
    "\n",
    "# Getting the data set containing claims amount\n",
    "# Link: https://www.openml.org/d/41215\n",
    "claims_data, _ = fetch_openml(\n",
    "    data_id=41215, as_frame=True, cache=True, data_home=\"~/.cache/sklearn\", return_X_y=True\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Then, some transformation needs to be made over that downloaded data. They include:\n",
    "- making proper changes so that merging the data-sets is possible\n",
    "- only keeping part of the data-set for faster computations\n",
    "- filtering unknown and null values for claim amounts\n",
    "- removing outliers\n",
    "\n",
    "Those steps are very useful for making the model better fit the data."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Set IDpol as index\n",
    "risks_data[\"IDpol\"] = risks_data[\"IDpol\"].astype(int)\n",
    "risks_data.set_index(\"IDpol\", inplace=True)\n",
    "\n",
    "# Grouping claims mounts together if they are associated with the same policy\n",
    "claims_data = claims_data.groupby(\"IDpol\").sum()\n",
    "\n",
    "# Merging the two sets over policy IDs\n",
    "data = risks_data.join(claims_data, how=\"left\")\n",
    "\n",
    "# Only keeping the first 100 000 for faster running time\n",
    "data = data.head(100000)\n",
    "\n",
    "# Filtering out unknown claim amounts\n",
    "data[\"ClaimAmount\"].fillna(0, inplace=True)\n",
    "\n",
    "# Filtering out claims with zero amount, as the severity (gamma) model\n",
    "# requires strictly positive target values\n",
    "data.loc[(data[\"ClaimAmount\"] == 0) & (data[\"ClaimNb\"] >= 1), \"ClaimNb\"] = 0\n",
    "\n",
    "# Removing unreasonable outliers\n",
    "data[\"ClaimNb\"] = data[\"ClaimNb\"].clip(upper=4)\n",
    "data[\"Exposure\"] = data[\"Exposure\"].clip(upper=1)\n",
    "data[\"ClaimAmount\"] = data[\"ClaimAmount\"].clip(upper=200000)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Transforming the data\n",
    "\n",
    "We now proceed by transforming the raw features into ones that can be given to a regression model. Thus, the categorical features are transformed using one-hot encoding while the resolution of vehicle and person ages is reduced using binning. Transforming the data this way, we end up with a total of 74 continuous features instead of the initial 12 ones."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "sklearn_sparse_arg = (\n",
    "    {\"sparse\": False} if \"1.1.\" in sklearn.__version__ else {\"sparse_output\": False}\n",
    ")\n",
    "log_scale_transformer = make_pipeline(FunctionTransformer(np.log, validate=False), StandardScaler())\n",
    "\n",
    "linear_model_preprocessor = ColumnTransformer(\n",
    "    [\n",
    "        (\"passthrough_numeric\", \"passthrough\", [\"BonusMalus\"]),\n",
    "        (\"binned_numeric\", KBinsDiscretizer(n_bins=10), [\"VehAge\", \"DrivAge\"]),\n",
    "        (\"log_scaled_numeric\", log_scale_transformer, [\"Density\"]),\n",
    "        (\n",
    "            \"onehot_categorical\",\n",
    "            OneHotEncoder(**sklearn_sparse_arg),\n",
    "            [\"VehBrand\", \"VehPower\", \"VehGas\", \"Region\", \"Area\"],\n",
    "        ),\n",
    "    ],\n",
    "    remainder=\"drop\",\n",
    ")\n",
    "\n",
    "x = linear_model_preprocessor.fit_transform(data)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Creating the target values\n",
    "\n",
    "Each GLM regressors expect different kind of distributions for the target values:\n",
    "- **Poisson** is a discrete model that expects target values to be non-negative and to follow a Poisson distribution. It can be used for modelling counts or relative frequencies (if scaled). This model is equivalent to a Tweedie model with power set to 1.\n",
    "- **Gamma** is a continuous model that expects target values to be (strictly) positive and to follow a Gamma distribution. It is often useful when values are skewed. This model is equivalent to a Tweedie model with power set to 2.\n",
    "- **Tweedie** is a continuous model that expects target values to follow a Tweedie distribution of fixed *power*. Depending on its *power* value, target values should either be non-negative or (strictly) positive. It can be useful for heavier tailed distribution when *power* is set to 3.\n",
    "\n",
    "We therefore create target values so that they each follow the desired distributions:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Creating target values for Poisson\n",
    "data[\"Frequency\"] = data[\"ClaimNb\"] / data[\"Exposure\"]\n",
    "\n",
    "# Creating target values for Gamma\n",
    "data[\"AvgClaimAmount\"] = data[\"ClaimAmount\"] / np.fmax(data[\"ClaimNb\"], 1)\n",
    "\n",
    "# Creating target values for Tweedie\n",
    "# Insurances companies are interested in modeling the Pure Premium, that is the expected total\n",
    "# claim amount per unit of exposure for each policyholder in their portfolio\n",
    "data[\"PurePremium\"] = data[\"ClaimAmount\"] / data[\"Exposure\"]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We can then plot their histograms on a log scale and confirm that they each approximately follow the desired distributions: an exponential distribution, with more skewed values for the AvgClaimAmount variable as well as a heavy tail for the PurePremium one:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAABTwAAAKgCAYAAAC2kLIhAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjcuNSwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/xnp5ZAAAACXBIWXMAAA9hAAAPYQGoP6dpAACQ1klEQVR4nOzde1iUdf7/8degHFNQQ0ETj6WFGbaIZCexEEKz01rWdkDdrGyoLbJW+n1Xs5PVbq7tNmW1i5TbQW03200z8ZRlHlAzM9LVltyyRM0EOQgI9++PLmadOM4wcA/3PB/XxVX3PZ+55z1vZu7P+GLu+7YZhmEIAAAAAAAAACwgwOwCAAAAAAAAAMBbCDwBAAAAAAAAWAaBJwAAAAAAAADLIPAEAAAAAAAAYBkEngAAAAAAAAAsg8ATAAAAAAAAgGUQeAIAAAAAAACwDAJPAAAAAAAAAJZB4AkAAAAAAADAMgg8AQAAvOyRRx6RzWZzWXfy5Ek99NBDiomJUUBAgK655hpJUklJiW6//XZFR0fLZrPpvvvua/uCYbqvv/5aNptNOTk5Zpfiln79+mnSpEnO5XXr1slms2ndunWt/tj1vc9sNpsyMjJa/bElKScnRzabTV9//XWbPB4AAGg+Ak8AAIBG1IYatT8hISHq1auXUlNT9ac//UnHjx9v1nays7P1+9//XhMmTNCrr76q+++/X5L05JNPKicnR9OmTdPChQt16623tubTaZE33nhD8+bNa/b4J598UkuXLm21erytvdVrJb7ce1+uDQAA1M9mGIZhdhEAAAC+KicnR5MnT9ajjz6q/v37q6qqSgcPHtS6deuUm5urPn366J///KfOO+88531OnjypkydPKiQkxLnuxhtv1Mcff6xvv/3WZfsXXHCBOnbsqI8//rjNnpOnrrzySu3atavZ32jr1KmTJkyY0G6+tWhmvV9//bX69++vBQsWuHxj0tf169dPSUlJzp7V1NSosrJSQUFBCgho/ncrPOl9fe8zm80mu92u559/vtnb8bS26upqVVVVKTg4uM43TQEAgLk6ml0AAABAe5CWlqbhw4c7l7OysrRmzRpdeeWVuuqqq/Tll18qNDRUktSxY0d17Oj6MevQoUPq0qVLne0eOnRIsbGxXquzNnA6NQSyktLSUp122mlml4EGBAQEtPprr/Y1UN/7rC116NBBHTp0MO3xAQBAwzikHQAAwEOXXXaZfve732n//v3629/+5lx/6rkFa8/NuHbtWn3xxRfOQ+Nrz3VYUFCgZcuWOdfXfnuyoqJCs2bN0plnnqng4GDFxMTooYceUkVFhUsNtecsfP311zVkyBAFBwdrxYoVkqQDBw5oypQpioqKUnBwsIYMGaLs7GyX+9fWsXjxYj3xxBPq3bu3QkJCdPnll2vfvn3OcUlJSVq2bJn279/vrLVfv34N9sZms6m0tFSvvvqqc3ztNxf379+vu+++W4MHD1ZoaKhOP/10XX/99XW+OVp7OoEPP/xQd999t3r06KHevXs7b3c4HBowYIBCQ0M1YsQIffTRR0pKSlJSUpLLdprTy8bq/bnCwkJ17NhRs2fPrnPbnj17ZLPZnN8wPHr0qKZPn66hQ4eqU6dOCg8PV1pamj777LMGe1ervuciSZMmTarT+5qaGs2bN09DhgxRSEiIoqKidOedd+rHH390Gbd161alpqYqMjJSoaGh6t+/v6ZMmdJkLYZh6PHHH1fv3r0VFham0aNH64svvqgzrr5zeO7du1e//OUvFR0drZCQEPXu3Vs33nijioqKJDXe+9r3Un5+vn71q1+pa9euuvjii11uq8/rr7+uwYMHKyQkRPHx8Vq/fn2TPaxvm43V1tA5PF944QXne7FXr16y2+06duyYy5ikpCSde+65ys/P1+jRoxUWFqYzzjhDzzzzTL3PBwAAuIdveAIAALTArbfeqocfflgrV67U1KlT69zevXt3LVy4UE888YRKSko0Z84cSdI555yjhQsX6v7771fv3r31wAMPOMfX1NToqquu0scff6w77rhD55xzjj7//HP98Y9/1L///e865xNcs2aNFi9erIyMDEVGRqpfv34qLCzUBRdc4AxEu3fvrvfff1+//vWvVVxcXOfiSE899ZQCAgI0ffp0FRUV6ZlnntHNN9+szZs3S5L+3//7fyoqKtK3336rP/7xj5J+OtS3IQsXLtTtt9+uESNG6I477pAkDRw4UJKUl5enTz75RDfeeKN69+6tr7/+Wi+++KKSkpKUn5+vsLAwl23dfffd6t69u2bOnKnS0lJJ0osvvqiMjAxdcskluv/++/X111/rmmuuUdeuXV1C0eb2srF6fy4qKkqjRo3S4sWLNWvWLJfbFi1apA4dOuj666+XJP3nP//R0qVLdf3116t///4qLCzUSy+9pFGjRik/P1+9evVqsIfuuPPOO52nX7j33ntVUFCg559/Xp9++qk2bNigwMBAHTp0SCkpKerevbtmzJihLl266Ouvv9Y//vGPJrc/c+ZMPf744xo7dqzGjh2r7du3KyUlRZWVlY3er7KyUqmpqaqoqNA999yj6OhoHThwQO+9956OHTumiIiIZvX++uuv11lnnaUnn3xSTZ2R68MPP9SiRYt07733Kjg4WC+88IKuuOIKbdmyReeee26Tz/VU7rwupJ8C09mzZys5OVnTpk3Tnj179OKLLyovL8/5e6j1448/6oorrtB1112nG264QW+//bZ++9vfaujQoUpLS3OrTgAA8DMGAAAAGrRgwQJDkpGXl9fgmIiICOP88893Ls+aNcv4+cesUaNGGUOGDKlz3759+xrjxo1zWbdw4UIjICDA+Oijj1zWz58/35BkbNiwwblOkhEQEGB88cUXLmN//etfGz179jSOHDnisv7GG280IiIijLKyMsMwDGPt2rWGJOOcc84xKioqnOOee+45Q5Lx+eefO9eNGzfO6Nu3b4N9+LnTTjvNSE9Pr7O+9rFPtXHjRkOS8dprrznX1fb+4osvNk6ePOlcX1FRYZx++ulGQkKCUVVV5Vyfk5NjSDJGjRrlXOdOLxuqtz4vvfRSnf4YhmHExsYal112mXP5xIkTRnV1tcuYgoICIzg42Hj00Udd1kkyFixY4Fw3atQol+dSKz093eX38NFHHxmSjNdff91l3IoVK1zWv/POO02+lutz6NAhIygoyBg3bpxRU1PjXP/www8bklx6Vvt6Wrt2rWEYhvHpp58akowlS5Y0+hgN9b72vXTTTTc1eNupJBmSjK1btzrX7d+/3wgJCTGuvfZa57qf97CxbTZUW+3rs6CgwDCM//UpJSXF5Xf+/PPPG5KM7Oxs57pRo0bVeb1XVFQY0dHRxi9/+cs6jwUAANzDIe0AAAAt1KlTp2Zfrb05lixZonPOOUdnn322jhw54vy57LLLJElr1651GT9q1CiX84AahqG///3vGj9+vAzDcNlGamqqioqKtH37dpdtTJ48WUFBQc7lSy65RNJP31D0ttpznUpSVVWVfvjhB5155pnq0qVLnbokaerUqS7nSty6dat++OEHTZ061eUcjjfffLO6du3qcl93e9lc1113nTp27KhFixY51+3atUv5+fmaOHGic11wcLDz4j3V1dX64Ycf1KlTJw0ePLje5+qJJUuWKCIiQmPGjHF5jvHx8erUqZPzOdaeQ/a9995TVVVVs7e/atUqVVZW6p577nE53Pvn3xKuT0REhCTpgw8+UFlZWfOf1M/cddddzR47cuRIxcfHO5f79Omjq6++Wh988IGqq6s9rqEptX267777XC7YNHXqVIWHh2vZsmUu4zt16qRbbrnFuRwUFKQRI0a0ynsOAAB/Q+AJAADQQiUlJercubPXtrd371598cUX6t69u8vPoEGDJP10oaNT9e/f32X58OHDOnbsmF5++eU625g8eXK92+jTp4/Lcm1w+PNzQHpDeXm5Zs6cqZiYGAUHBysyMlLdu3fXsWPHnOd1PNXPn9/+/fslSWeeeabL+o4dO9Y5L6O7vWyuyMhIXX755Vq8eLFz3aJFi9SxY0ddd911znU1NTX64x//qLPOOsvlue7cubPe5+qJvXv3qqioSD169KjzPEtKSpzPcdSoUfrlL3+p2bNnKzIyUldffbUWLFhQ57ywP1fb77POOstlfffu3esEzD/Xv39/ZWZm6i9/+YsiIyOVmpoqh8Ph9nP/+WugMT+vU5IGDRqksrIyHT582K3HdUdtnwYPHuyyPigoSAMGDHDeXqt37951zkHatWvXVnnPAQDgbziHJwAAQAt8++23KioqqhO+tURNTY2GDh2quXPn1nt7TEyMy/Kp35isvb8k3XLLLUpPT693G+edd57LckNXmzaaOF+iJ+655x4tWLBA9913n0aOHKmIiAjZbDbdeOONztpP9fPn5w53e+mOG2+8UZMnT9aOHTs0bNgwLV68WJdffrkiIyOdY5588kn97ne/05QpU/TYY4+pW7duCggI0H333Vfvcz2VzWart/8//5ZiTU2NevTooddff73e7XTv3t25vbffflubNm3Sv/71L33wwQeaMmWKnn32WW3atKnRc7K2xLPPPqtJkybp3Xff1cqVK3Xvvfdqzpw52rRpk8v5VhvTktdAfRq62FFrfgP059ryPQcAgL8h8AQAAGiBhQsXSpJSU1O9ts2BAwfqs88+0+WXX95gMNOY7t27q3PnzqqurlZycrLX6nK3lobGv/3220pPT9ezzz7rXHfixIk6V7JuSN++fSVJ+/bt0+jRo53rT548qa+//tolzHWnl+4+v2uuuUZ33nmn87D2f//738rKynIZ8/bbb2v06NH661//6rL+2LFjLsFofbp27Vrv4c0//6bgwIEDtWrVKl100UXNCgYvuOACXXDBBXriiSf0xhtv6Oabb9Zbb72l22+/vd7xtf3eu3evBgwY4Fx/+PDhZn8bcejQoRo6dKj+7//+T5988okuuugizZ8/X48//rgk93vfmL1799ZZ9+9//1thYWHO8Ldr1671vt5+3lt3aqvt0549e1z6VFlZqYKCAq++FwEAQOM4pB0AAMBDa9as0WOPPab+/fvr5ptv9tp2b7jhBh04cECvvPJKndvKy8udVypvSIcOHfTLX/5Sf//737Vr1646t3t6WO9pp53m1qHIp512Wr2hUocOHep8i+3Pf/5zs79dN3z4cJ1++ul65ZVXdPLkSef6119/vU4A504vG6q3IV26dFFqaqoWL16st956S0FBQbrmmmtcxtT3XJcsWaIDBw40uf2BAwdq9+7dLr+vzz77TBs2bHAZd8MNN6i6ulqPPfZYnW2cPHnS+Zx+/PHHOrUMGzZMkho9rD05OVmBgYH685//7HL/efPmNfkciouLXX5H0k/hZ0BAgMtjutv7xmzcuNHl/KjffPON3n33XaWkpDi/VTlw4EAVFRVp586dznHff/+93nnnnTrba25tycnJCgoK0p/+9CeXPv31r39VUVGRxo0b14JnBQAA3ME3PAEAAJrh/fff1+7du3Xy5EkVFhZqzZo1ys3NVd++ffXPf/5TISEhXnusW2+9VYsXL9Zdd92ltWvX6qKLLlJ1dbV2796txYsX64MPPtDw4cMb3cZTTz2ltWvXKjExUVOnTlVsbKyOHj2q7du3a9WqVTp69KjbdcXHx2vRokXKzMxUQkKCOnXqpPHjxzc6ftWqVZo7d6569eql/v37KzExUVdeeaUWLlyoiIgIxcbGauPGjVq1apVOP/30ZtURFBSkRx55RPfcc48uu+wy3XDDDfr666+Vk5OjgQMHunwjz51eNlRvYyZOnKhbbrlFL7zwglJTU50XBqp15ZVX6tFHH9XkyZN14YUX6vPPP9frr7/u8g3AhkyZMkVz585Vamqqfv3rX+vQoUOaP3++hgwZouLiYue4UaNG6c4779ScOXO0Y8cOpaSkKDAwUHv37tWSJUv03HPPacKECXr11Vf1wgsv6Nprr9XAgQN1/PhxvfLKKwoPD9fYsWMbrKN79+6aPn265syZoyuvvFJjx47Vp59+qvfff7/Jb6muWbNGGRkZuv766zVo0CCdPHlSCxcudIbytTzpfUPOPfdcpaam6t5771VwcLBeeOEFSdLs2bOdY2688Ub99re/1bXXXqt7771XZWVlevHFFzVo0KA6F5Nqbm3du3dXVlaWZs+erSuuuEJXXXWV9uzZoxdeeEEJCQkuFygCAACtzKzLwwMAALQHCxYsMCQ5f4KCgozo6GhjzJgxxnPPPWcUFxfXuc+sWbOMn3/MGjVqlDFkyJA6Y/v27WuMGzeuzvrKykrj6aefNoYMGWIEBwcbXbt2NeLj443Zs2cbRUVFznGSDLvdXm/thYWFht1uN2JiYozAwEAjOjrauPzyy42XX37ZOWbt2rWGJGPJkiUu9y0oKDAkGQsWLHCuKykpMX71q18ZXbp0MSQZffv2rfdxa+3evdu49NJLjdDQUEOSkZ6ebhiGYfz444/G5MmTjcjISKNTp05GamqqsXv3bqNv377OMYbxv97n5eXVu/0//elPRt++fY3g4GBjxIgRxoYNG4z4+Hjjiiuu8KiXDdXbmOLiYuf4v/3tb3VuP3HihPHAAw8YPXv2NEJDQ42LLrrI2LhxozFq1Chj1KhRznH19dswDONvf/ubMWDAACMoKMgYNmyY8cEHHxjp6en19v7ll1824uPjjdDQUKNz587G0KFDjYceesj47rvvDMMwjO3btxs33XST0adPHyM4ONjo0aOHceWVVxpbt25t8nlWV1cbs2fPdj6PpKQkY9euXXV+Z7Wvp7Vr1xqGYRj/+c9/jClTphgDBw40QkJCjG7duhmjR482Vq1a5bL9hnpf+146fPhwnZrqe5/Vvh/+9re/GWeddZYRHBxsnH/++c56TrVy5Urj3HPPNYKCgozBgwcbf/vb3+rdZkO11b4+CwoKXMY///zzxtlnn20EBgYaUVFRxrRp04wff/zRZUxD+4OGfrcAAMA9NsPgrNgAAABo/2pqatS9e3ddd9119R7CDgAAAP/AOTwBAADQ7pw4caLO+Shfe+01HT16VElJSeYUBQAAAJ/ANzwBAADQ7qxbt07333+/rr/+ep1++unavn27/vrXv+qcc87Rtm3bFBQUZHaJAAAAMAkXLQIAAEC7069fP8XExOhPf/qTjh49qm7duum2227TU089RdgJAADg5/iGJwAAAAAAAADL4ByeAAAAAAAAACyDwBMAAAAAAACAZRB4AgAAAAAAALAMAk8AAAAAAAAAlkHgCQAAAAAAAMAyCDwBAAAAAAAAWAaBJwAAAAAAAADLIPAEAAAAAAAAYBkEngAAAAAAAAAsg8ATAAAAAAAAgGUQeAIAAAAAAACwDAJPAAAAAAAAAJZB4AkAAAAAAADAMgg8AQAAAAAAAFgGgScAAAAAAAAAyyDwBAAAAAAAAGAZBJ4AAAAAAAAALIPAEwAAAAAAAIBlEHgCAAAAAAAAsAwCTwAAAAAAAACWQeAJAAAAAAAAwDIIPAEAAAAAAABYBoEnAAAAAAAAAMsg8AQAAAAAAABgGQSeAAAAAAAAACyDwBMAAAAAAACAZRB4AgAAAAAAALAMAk8AAAAAAAAAlkHgCQAAAAAAAMAyCDwBoAFJSUlKSkoyuwwAAAAA8Gn9+vXTpEmTnMvr1q2TzWbTunXrTKsJ/o3AE4Cl5OTkyGazOX9CQkI0aNAgZWRkqLCw0OzyAAAWUFBQoIyMDA0aNEhhYWEKCwtTbGys7Ha7du7caXZ5AIB25tR/vzT2Q3gINF9HswsAgNbw6KOPqn///jpx4oQ+/vhjvfjii1q+fLl27dqlsLCwZm1j5cqVrVwlAKC9ee+99zRx4kR17NhRN998s+Li4hQQEKDdu3frH//4h1588UUVFBSob9++ZpcKAGgnFi5c6LL82muvKTc3t876c845py3LapFLL71U5eXlCgoKMrsU+CkCTwCWlJaWpuHDh0uSbr/9dp1++umaO3eu3n33Xd10003N2gaTMwDgVF999ZVuvPFG9e3bV6tXr1bPnj1dbn/66af1wgsvKCCAg6gAAM13yy23uCxv2rRJubm5dda3JwEBAQoJCTG7DPgxPo0B8AuXXXaZpJ8OQzx58qQee+wxDRw4UMHBwerXr58efvhhVVRUuNynvnN4/vnPf9aQIUMUFhamrl27avjw4XrjjTectx8/flz33Xef+vXrp+DgYPXo0UNjxozR9u3bXbazZMkSxcfHKzQ0VJGRkbrlllt04MABlzGTJk1Sp06ddODAAV1zzTXq1KmTunfvrunTp6u6utqL3QEANMczzzyj0tJSLViwoE7YKUkdO3bUvffeq5iYGEnSzp07NWnSJA0YMEAhISGKjo7WlClT9MMPP7jc75FHHpHNZtO///1v3XLLLYqIiFD37t31u9/9ToZh6JtvvtHVV1+t8PBwRUdH69lnn3W5f+150hYvXqzZs2frjDPOUOfOnTVhwgQVFRWpoqJC9913n3r06KFOnTpp8uTJdea8BQsW6LLLLlOPHj0UHBys2NhYvfjii17uIADAE9ddd51+8YtfuKwbP368bDab/vnPfzrXbd68WTabTe+//75z3bFjx3TfffcpJiZGwcHBOvPMM/X000+rpqbGZXs1NTWaN2+ehgwZopCQEEVFRenOO+/Ujz/+6DLOMAw9/vjj6t27t8LCwjR69Gh98cUXdWpu6Byemzdv1hVXXKGIiAiFhYVp1KhR2rBhg6etARrENzwB+IWvvvpKknT66afr9ttv16uvvqoJEybogQce0ObNmzVnzhx9+eWXeueddxrcxiuvvKJ7771XEyZM0G9+8xudOHFCO3fu1ObNm/WrX/1KknTXXXfp7bffVkZGhmJjY/XDDz/o448/1pdffun8kJKTk6PJkycrISFBc+bMUWFhoZ577jlt2LBBn376qbp06eJ8zOrqaqWmpioxMVF/+MMftGrVKj377LMaOHCgpk2b1noNAwDU8d577+nMM89UYmJis8bn5ubqP//5jyZPnqzo6Gh98cUXevnll/XFF19o06ZNstlsLuMnTpyoc845R0899ZSWLVumxx9/XN26ddNLL72kyy67TE8//bRef/11TZ8+XQkJCbr00ktd7j9nzhyFhoZqxowZ2rdvn/785z8rMDBQAQEB+vHHH/XII49o06ZNysnJUf/+/TVz5kznfV988UUNGTJEV111lTp27Kh//etfuvvuu1VTUyO73d7y5gEAPHbJJZfo3XffVXFxscLDw2UYhjZs2KCAgAB99NFHuuqqqyRJH330kQICAnTRRRdJksrKyjRq1CgdOHBAd955p/r06aNPPvlEWVlZ+v777zVv3jznY9x5553Of6fce++9Kigo0PPPP69PP/1UGzZsUGBgoCRp5syZevzxxzV27FiNHTtW27dvV0pKiiorK5t8HmvWrFFaWpri4+M1a9YsBQQEOP/g9tFHH2nEiBHebx78lwEAFrJgwQJDkrFq1Srj8OHDxjfffGO89dZbxumnn26EhoYa69atMyQZt99+u8v9pk+fbkgy1qxZ41w3atQoY9SoUc7lq6++2hgyZEijjx8REWHY7fYGb6+srDR69OhhnHvuuUZ5eblz/XvvvWdIMmbOnOlcl56ebkgyHn30UZdtnH/++UZ8fHyjdQAAvKuoqMiQZFxzzTV1bvvxxx+Nw4cPO3/KysoMwzCc/z3Vm2++aUgy1q9f71w3a9YsQ5Jxxx13ONedPHnS6N27t2Gz2YynnnrK5bFCQ0ON9PR057q1a9cakoxzzz3XqKysdK6/6aabDJvNZqSlpbnUMHLkSKNv374u6+qrNTU11RgwYEADHQEAtBa73W6cGtfk5eUZkozly5cbhmEYO3fuNCQZ119/vZGYmOgcd9VVVxnnn3++c/mxxx4zTjvtNOPf//63y/ZnzJhhdOjQwfjvf/9rGIZhfPTRR4Yk4/XXX3cZt2LFCpf1hw4dMoKCgoxx48YZNTU1znEPP/ywIaneuWnt2rWGYRhGTU2NcdZZZxmpqaku9y0rKzP69+9vjBkzxpNWAQ3ikHYAlpScnKzu3bsrJiZGN954ozp16qR33nlHn3zyiSQpMzPTZfwDDzwgSVq2bFmD2+zSpYu+/fZb5eXlNTpm8+bN+u677+q9fevWrTp06JDuvvtul3PajBs3TmeffXa9j3/XXXe5LF9yySX6z3/+02ANAADvKy4uliR16tSpzm1JSUnq3r2788fhcEiSQkNDnWNOnDihI0eO6IILLpCkOqc6kX4653StDh06aPjw4TIMQ7/+9a+d67t06aLBgwfXOw/cdtttzm/gSFJiYqIMw9CUKVNcxiUmJuqbb77RyZMnnetOrbWoqEhHjhzRqFGj9J///EdFRUUNdAUA0BbOP/98derUSevXr5f00zc5e/furdtuu03bt29XWVmZDMPQxx9/rEsuucR5vyVLluiSSy5R165ddeTIEedPcnKyqqurndtbsmSJIiIiNGbMGJdx8fHx6tSpk9auXStJWrVqlSorK3XPPfe4HKVw3333NfkcduzYob179+pXv/qVfvjhB+djlJaW6vLLL9f69evrHGYPtASHtAOwJIfDoUGDBqljx46KiorS4MGDFRAQoHfeeUcBAQE688wzXcZHR0erS5cu2r9/f4Pb/O1vf6tVq1ZpxIgROvPMM5WSkqJf/epXzkNGpJ/O75aenq6YmBjFx8dr7Nixuu222zRgwABJcm5/8ODBdbZ/9tln6+OPP3ZZFxISou7du7us69q1a51z6QAAWlfnzp0lSSUlJXVue+mll3T8+HEVFha6XGDi6NGjmj17tt566y0dOnTI5T71hYh9+vRxWY6IiFBISIgiIyPrrP/5eUAbur8k5zlFT11fU1OjoqIinX766ZKkDRs2aNasWdq4caPKysrq1Fq7LQBA2+vQoYNGjhypjz76SNJPgecll1yiiy++WNXV1dq0aZOioqJ09OhRl8Bz79692rlzZ51/T9SqnZv27t2roqIi9ejRo9Fxtf+WOeuss1xu7969u7p27droc9i7d68kKT09vcExRUVFTW4HaC4CTwCWNGLECOdV2uvz8/OmNcc555yjPXv26L333tOKFSv097//XS+88IJmzpyp2bNnS5JuuOEGXXLJJXrnnXe0cuVK/f73v9fTTz+tf/zjH0pLS3P7MTt06OD2fQAA3hcREaGePXtq165ddW6rPafn119/7bL+hhtu0CeffKIHH3xQw4YNU6dOnVRTU6Mrrrii3m+x1LfPb2geMAyj2WOb2sZXX32lyy+/XGeffbbmzp2rmJgYBQUFafny5frjH//IN24AwAdcfPHFeuKJJ3TixAl99NFH+n//7/+pS5cuOvfcc/XRRx8pKipKklwCz5qaGo0ZM0YPPfRQvdscNGiQc1yPHj30+uuv1zuuocDUHbVzye9//3sNGzas3jH1HUUBeIrAE4Bf6du3r2pqarR3716dc845zvWFhYU6duyY+vbt2+j9TzvtNE2cOFETJ05UZWWlrrvuOj3xxBPKyspyHqLes2dP3X333br77rt16NAh/eIXv9ATTzyhtLQ05/b37NnjvHJ8rT179jT5+AAA84wbN05/+ctftGXLliYvrPDjjz9q9erVmj17tsvFgWq/4eJL/vWvf6miokL//Oc/Xb4lWnsIIwDAfJdccokqKyv15ptv6sCBA85g89JLL3UGnoMGDXIGn5I0cOBAlZSUKDk5udFtDxw4UKtWrdJFF13kcoqTn6v9t8revXudR7BJ0uHDh5s8Am3gwIGSpPDw8CbrAbyBc3gC8Ctjx46VJJcrEkrS3LlzJf30j9mG/PzwwaCgIMXGxsowDFVVVam6urrOIYo9evRQr169VFFRIUkaPny4evToofnz5zvXSdL777+vL7/8stHHBwCY66GHHlJYWJimTJmiwsLCOref+q3L2m9V/vyb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mzZtUm5urqqqqpSSkqLS0lLnGF96X/LZsXHu7tua81r1Z57MFeHh4S77mP3797dhxb6vOfvpUzVn3+Hv3O1prT179ri8Vnv06NFKFVqIAVjMiBEjDLvd7lyurq42evXqZcyZM8fEqtqHWbNmGXFxcfXeduzYMSMwMNBYsmSJc92XX35pSDI2btzYRhW2L5KMd955x7lcU1NjREdHG7///e+d644dO2YEBwcbb775pmEYhpGfn29IMvLy8pxj3n//fcNmsxkHDhxos9p91c97ahiGkZ6eblx99dUN3oeewtv8aZ5p6bywfPlyIyAgwDh48KBzzIsvvmiEh4cbFRUVhmEYxkMPPWQMGTLEZdsTJ040UlNTncvtqeette9/4YUXjK5duzr7ZhiG8dvf/tYYPHiwc/mGG24wxo0b51JPYmKiceeddza7FrO01v7d6n07dOiQIcn48MMPnXX5yvuSz45Nc3ff1tRr1d+5288FCxYYERERbVRd+1fffvrnmrPvwP80p6dr1641JBk//vhjm9RkJXzDE5ZSWVmpbdu2KTk52bkuICBAycnJ2rhxo4mVtR979+5Vr169NGDAAN18883673//K0natm2bqqqqXHp79tlnq0+fPvS2mQoKCnTw4EGXHkZERCgxMdHZw40bN6pLly4aPny4c0xycrICAgK0efPmNq+5vVi3bp169OihwYMHa9q0afrhhx+ct9FTeJM/zjMtmRc2btyooUOHKioqyjkmNTVVxcXF+uKLL5xjTt1G7ZjabbT3nntr379x40ZdeumlCgoKco5JTU3Vnj179OOPPzrHNNbL5tTia1q6f7d634qKiiRJ3bp1k+Rb70s+OzbOk31bU78Xf+bpXFFSUqK+ffsqJiZGV199tfM9AM/wGm09w4YNU8+ePTVmzBht2LDB7HLaBQJPWMqRI0dUXV3t8gFOkqKiojjHRTMkJiYqJydHK1as0IsvvqiCggJdcsklOn78uA4ePKigoCB16dLF5T70tvlq+9TY6/PgwYN1Dk/o2LGjunXrRp8bcMUVV+i1117T6tWr9fTTT+vDDz9UWlqaqqurJdFTeJe/zTMtnRcOHjxYb69qb2tsTHFxscrLy9t9z721729JL0+9valafIk39u9W7ltNTY3uu+8+XXTRRTr33HMlyafel3x2bJwn+7amXqv+zJN+Dh48WNnZ2Xr33Xf1t7/9TTU1Nbrwwgv17bfftkXJltTUvgPu69mzp+bPn6+///3v+vvf/66YmBglJSVp+/btZpfm8zqaXQAA35GWlub8//POO0+JiYnq27evFi9erNDQUBMrAxp24403Ov9/6NChOu+88zRw4ECtW7dOl19+uYmVAe0f8wLMxP69cXa7Xbt27XI5ry6A5hs5cqRGjhzpXL7wwgt1zjnn6KWXXtJjjz1mYmXA/wwePFiDBw92Ll944YX66quv9Mc//lELFy40sTLfxzc8YSmRkZHq0KFDnas/FhYWKjo62qSq2q8uXbpo0KBB2rdvn6Kjo1VZWaljx465jKG3zVfbp8Zen9HR0XVOrH7y5EkdPXqUPjfTgAEDFBkZqX379kmip/Auf59n3J0XoqOj6+1V7W2NjQkPD1doaGi777m39v0t6eWptzdViy/zZP9u1b5lZGTovffe09q1a9W7d2/nel96X/LZsXGe7Nuaeq36M2/MFYGBgTr//POd+xi4r6l9B7xjxIgRvE6bgcATlhIUFKT4+HitXr3aua6mpkarV692+esdmqekpERfffWVevbsqfj4eAUGBrr0ds+ePfrvf/9Lb5upf//+io6OdulhcXGxNm/e7OzhyJEjdezYMW3bts05Zs2aNaqpqVFiYmKb19weffvtt/rhhx/Us2dPSfQU3uXv84y788LIkSP1+eefu4RSubm5Cg8PV2xsrHPMqduoHVO7jfbec2/t+0eOHKn169erqqrKOSY3N1eDBw9W165dnWMa62VzavFlnuzfrdY3wzCUkZGhd955R2vWrFH//v1dbvel9yWfHRvnyb6tqd+LP/PGXFFdXa3PP//cuY+B+3iNto0dO3bwOm0Os6+aBHjbW2+9ZQQHBxs5OTlGfn6+cccddxhdunRxuRIl6vfAAw8Y69atMwoKCowNGzYYycnJRmRkpHHo0CHDMAzjrrvuMvr06WOsWbPG2Lp1qzFy5Ehj5MiRJlftW44fP258+umnxqeffmpIMubOnWt8+umnxv79+w3DMIynnnrK6NKli/Huu+8aO3fuNK6++mqjf//+Rnl5uXMbV1xxhXH++ecbmzdvNj7++GPjrLPOMm666SaznpLpGuvp8ePHjenTpxsbN240CgoKjFWrVhm/+MUvjLPOOss4ceKEcxv0FN7kT/NMS+eFkydPGueee66R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",
      "text/plain": [
       "<Figure size 1500x700 with 3 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "plt.ioff()\n",
    "fig, ax = plt.subplots(1, 3, figsize=(15, 7))\n",
    "\n",
    "# Set the figure's main parameters\n",
    "fig.patch.set_facecolor(\"white\")\n",
    "fig.suptitle(\"Different target values distribution\")\n",
    "fig.supylabel(\"Count\")\n",
    "\n",
    "# Frequency of claims distribution\n",
    "ax[0].set_title(\"Poisson\")\n",
    "ax[0].set_xlabel(\"Frequency of claims\")\n",
    "data[\"Frequency\"].hist(bins=30, log=True, ax=ax[0], color=\"black\")\n",
    "\n",
    "# Average amount of claims distribution\n",
    "ax[1].set_title(\"Gamma\")\n",
    "ax[1].set_xlabel(\"Average amount of claims\")\n",
    "data[\"AvgClaimAmount\"].hist(bins=30, log=True, ax=ax[1], color=\"blue\")\n",
    "\n",
    "# PurePrenium distribution\n",
    "ax[2].set_title(\"Tweedie\")\n",
    "ax[2].set_xlabel(\"PurePrenium\")\n",
    "data[\"PurePremium\"].hist(bins=30, log=True, ax=ax[2], color=\"red\")\n",
    "\n",
    "display(fig)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Splitting the data\n",
    "\n",
    "We then split the data into a training and test set. The test set is then reduced for faster FHE runs."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [],
   "source": [
    "train_data, test_data, x_train_data, x_test_data = train_test_split(\n",
    "    data,\n",
    "    x,\n",
    "    test_size=0.2,\n",
    "    random_state=0,\n",
    ")\n",
    "_, test_data, _, x_test_data = train_test_split(\n",
    "    test_data,\n",
    "    x_test_data,\n",
    "    test_size=50,\n",
    "    random_state=0,\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Setting the right parameters\n",
    "\n",
    "Each regressors do neither share exactly the same parameers nor are trained over the same data (for example, Gamma only considers strictly positive target values):"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [],
   "source": [
    "gamma_mask_train = train_data[\"ClaimAmount\"] > 0\n",
    "gamma_mask_test = test_data[\"ClaimAmount\"] > 0\n",
    "\n",
    "\n",
    "parameters_glms = {\n",
    "    \"Poisson\": {\n",
    "        \"sklearn\": SklearnPoissonRegressor,\n",
    "        \"concrete\": ConcretePoissonRegressor,\n",
    "        \"init_parameters\": {\n",
    "            \"alpha\": 1e-3,\n",
    "            \"max_iter\": 400,\n",
    "        },\n",
    "        \"fit_parameters\": {\n",
    "            \"X\": x_train_data,\n",
    "            \"y\": train_data[\"Frequency\"],\n",
    "            \"sample_weight\": train_data[\"Exposure\"],\n",
    "        },\n",
    "        \"x_test\": x_test_data,\n",
    "        \"score_parameters\": {\n",
    "            \"y_true\": test_data[\"Frequency\"],\n",
    "            \"sample_weight\": test_data[\"Exposure\"],\n",
    "        },\n",
    "        \"deviance\": mean_poisson_deviance,\n",
    "    },\n",
    "    \"Gamma\": {\n",
    "        \"sklearn\": SklearnGammaRegressor,\n",
    "        \"concrete\": ConcreteGammaRegressor,\n",
    "        \"init_parameters\": {\n",
    "            \"alpha\": 10.0,\n",
    "            \"max_iter\": 300,\n",
    "        },\n",
    "        \"fit_parameters\": {\n",
    "            \"X\": x_train_data[gamma_mask_train],\n",
    "            \"y\": train_data[gamma_mask_train][\"AvgClaimAmount\"],\n",
    "            \"sample_weight\": train_data[gamma_mask_train][\"ClaimNb\"],\n",
    "        },\n",
    "        \"x_test\": x_test_data[gamma_mask_test],\n",
    "        \"score_parameters\": {\n",
    "            \"y_true\": test_data[gamma_mask_test][\"AvgClaimAmount\"],\n",
    "            \"sample_weight\": test_data[gamma_mask_test][\"ClaimNb\"],\n",
    "        },\n",
    "        \"deviance\": mean_gamma_deviance,\n",
    "    },\n",
    "    \"Tweedie\": {\n",
    "        \"sklearn\": SklearnTweedieRegressor,\n",
    "        \"concrete\": ConcreteTweedieRegressor,\n",
    "        \"init_parameters\": {\n",
    "            \"power\": 1.9,\n",
    "            \"alpha\": 0.1,\n",
    "            \"max_iter\": 10000,\n",
    "        },\n",
    "        \"fit_parameters\": {\n",
    "            \"X\": x_train_data,\n",
    "            \"y\": train_data[\"PurePremium\"],\n",
    "            \"sample_weight\": train_data[\"Exposure\"],\n",
    "        },\n",
    "        \"x_test\": x_test_data,\n",
    "        \"score_parameters\": {\n",
    "            \"y_true\": test_data[\"PurePremium\"],\n",
    "            \"sample_weight\": test_data[\"Exposure\"],\n",
    "            \"power\": 1.9,\n",
    "        },\n",
    "        \"deviance\": mean_tweedie_deviance,\n",
    "    },\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Comparing the models\n",
    "\n",
    "The following function iterates over the three GLM regressors and outputs scores from both the sklearn and concrete models for each regressors. Basically, it:\n",
    "- instantiates both models\n",
    "- fits both models\n",
    "- compiles the Concrete ML model if it needs to be executed in FHE\n",
    "- computes the predictions for both models\n",
    "- computes the scores for both models using the proper deviance function\n",
    "\n",
    "The score used here is the mean deviance score. It indicates the goodness-of-fit of a model and is widely used for evaluating the performances of a GLM. The lower the score, the better !"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [],
   "source": [
    "def compare_regressors(n_bits, fhe=\"simulate\"):\n",
    "    # pylint: disable=too-many-locals\n",
    "    scores = defaultdict(list)\n",
    "    predictions = defaultdict(list)\n",
    "\n",
    "    for glm, parameters_glm in parameters_glms.items():\n",
    "        # Retrieve the regressors\n",
    "        sklearn_class = parameters_glm[\"sklearn\"]\n",
    "        concrete_class = parameters_glm[\"concrete\"]\n",
    "\n",
    "        # Instantiate the models\n",
    "        init_parameters = parameters_glm[\"init_parameters\"]\n",
    "        sklearn_glm = sklearn_class(**init_parameters)\n",
    "        concrete_glm = concrete_class(n_bits=n_bits, **init_parameters)\n",
    "\n",
    "        # Fit the models\n",
    "        fit_parameters = parameters_glm[\"fit_parameters\"]\n",
    "        sklearn_glm.fit(**fit_parameters)\n",
    "        concrete_glm.fit(**fit_parameters)\n",
    "\n",
    "        x_train_subset = fit_parameters[\"X\"][:100]\n",
    "        # Compile the Concrete ML model if it needs to be executed in FHE\n",
    "        if fhe in [\"execute\", \"simulate\"]:\n",
    "            circuit = concrete_glm.compile(x_train_subset)\n",
    "\n",
    "            # Generate the key\n",
    "            print(\n",
    "                \"Generating a key for an \"\n",
    "                f\"{circuit.graph.maximum_integer_bit_width()}-bit circuit\"\n",
    "            )\n",
    "            sys.stdout.flush()\n",
    "\n",
    "            time_begin = time.time()\n",
    "            circuit.client.keygen(force=False)\n",
    "            print(f\"Key generation time: {time.time() - time_begin:.4f} seconds\")\n",
    "\n",
    "        # Compute the predictions using sklearn (floating points, in the clear)\n",
    "        x_test = parameters_glm[\"x_test\"]\n",
    "        sklearn_predictions = sklearn_glm.predict(x_test)\n",
    "\n",
    "        # Compute the predictions using Concrete ML (quantized, in the clear)\n",
    "        concrete_q_predictions = concrete_glm.predict(x_test)\n",
    "\n",
    "        # Compute the predictions using Concrete ML (in FHE)\n",
    "        start = timer()\n",
    "        concrete_predictions = concrete_glm.predict(\n",
    "            x_test,\n",
    "            fhe=fhe,\n",
    "        )\n",
    "        end = timer()\n",
    "        run_time = end - start\n",
    "\n",
    "        # Compute the deviance scores\n",
    "        mean_deviance = parameters_glm[\"deviance\"]\n",
    "        score_parameters = parameters_glm[\"score_parameters\"]\n",
    "        sklearn_score = mean_deviance(y_pred=sklearn_predictions, **score_parameters)\n",
    "        concrete_q_score = mean_deviance(y_pred=concrete_q_predictions, **score_parameters)\n",
    "        concrete_score = mean_deviance(y_pred=concrete_predictions, **score_parameters)\n",
    "\n",
    "        # Print the deviance scores\n",
    "        fhe_message = \"in FHE\" if fhe == \"execute\" else \"in clear\"\n",
    "        print(f\"Mean {glm} deviance (scikit-learn): {sklearn_score:.4f}\")\n",
    "        print(f\"Mean {glm} deviance (Concrete ML, quantized): {concrete_q_score:.4f}\")\n",
    "        print(\n",
    "            f\"Mean {glm} deviance (Concrete ML {fhe_message}, \"\n",
    "            f\"with {run_time / len(x_test):.4f} seconds \"\n",
    "            f\"per inference): {concrete_score:.4f}\"\n",
    "        )\n",
    "\n",
    "        # Measure the error of the FHE quantized model with respect to the clear scikit-learn\n",
    "        # float model\n",
    "        score_difference = abs(concrete_score - sklearn_score) * 100 / sklearn_score\n",
    "        print(\n",
    "            \"Relative difference between scikit-learn (clear) and Concrete-ml (FHE) scores:\",\n",
    "            f\"{score_difference:.2f}%\\n\",\n",
    "        )\n",
    "\n",
    "        # Store the results\n",
    "        scores[\"sklearn\"].append(sklearn_score)\n",
    "        scores[\"concrete\"].append(concrete_score)\n",
    "        predictions[\"sklearn\"].append(sklearn_predictions)\n",
    "        predictions[\"concrete\"].append(concrete_predictions)\n",
    "\n",
    "    return scores, predictions"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "As for all linear models available in Concrete ML, a user has to set the `n_bits` parameter for \n",
    "initialization. This parameter can either be:\n",
    "- a dictionary composed of `op_inputs` and `op_weights` keys. These parameters are given as \n",
    "    integers representing the number of bits over which the associated data should be quantized.\n",
    "\n",
    "- an integer, representing the number of bits over which each input and weight should be quantized.\n",
    "    Default is 8.\n",
    "\n",
    "Additionally, Concrete ML lets the users chose whether the model should be ran in clear or FHE using the `fhe` parameter. Setting it to \"disable\" will run the model on unencrypted data using the same quantization process."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Generating a key for an 25-bit circuit\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Key generation time: 0.0002 seconds\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Mean Poisson deviance (scikit-learn): 1.1568\n",
      "Mean Poisson deviance (Concrete ML, quantized): 1.1589\n",
      "Mean Poisson deviance (Concrete ML in FHE, with 0.0209 seconds per inference): 1.1589\n",
      "Relative difference between scikit-learn (clear) and Concrete-ml (FHE) scores: 0.19%\n",
      "\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Generating a key for an 24-bit circuit\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Key generation time: 0.0002 seconds\n",
      "Mean Gamma deviance (scikit-learn): 0.3179\n",
      "Mean Gamma deviance (Concrete ML, quantized): 0.3180\n",
      "Mean Gamma deviance (Concrete ML in FHE, with 0.0170 seconds per inference): 0.3180\n",
      "Relative difference between scikit-learn (clear) and Concrete-ml (FHE) scores: 0.03%\n",
      "\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Generating a key for an 25-bit circuit\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Key generation time: 0.0002 seconds\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Mean Tweedie deviance (scikit-learn): 37.6493\n",
      "Mean Tweedie deviance (Concrete ML, quantized): 37.7585\n",
      "Mean Tweedie deviance (Concrete ML in FHE, with 0.0176 seconds per inference): 37.7585\n",
      "Relative difference between scikit-learn (clear) and Concrete-ml (FHE) scores: 0.29%\n",
      "\n"
     ]
    }
   ],
   "source": [
    "n_bits = 11\n",
    "fhe = \"execute\"\n",
    "\n",
    "scores, predictions = compare_regressors(n_bits, fhe=fhe)"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Analysis\n",
    "We can observe that scikit-learn and Concrete ML models output have very close mean deviance scores. This demonstrate how the quantization process has a very limited impact on performances. "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Conclusion\n",
    "\n",
    "This notebook was greatly inspired by [this notebook](https://scikit-learn.org/stable/auto_examples/linear_model/plot_tweedie_regression_insurance_claims.html) from scikit-learn which showed how to use three Generalized Linear Model (GLM) regressors they implemented (Poisson, Gamma and Tweedie) in a real regression problem.\n",
    "\n",
    "We then modified some of its content in order to demonstrate how running these models in Fully Homomorphic Encryption (FHE) can be very similar to the sklearn workflow using the Concrete ML framework. \n",
    "\n",
    "Finally, comparing the mean deviance scores from both models over each regressors revealed how quantization has a negligible impact on their performances, thus making Concrete ML's models reach almost identical results with respect to their equivalent scikit-learn ones."
   ]
  }
 ],
 "metadata": {
  "execution": {
   "timeout": 10800
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
